<!DOCTYPE html>
<html>
<head hexo-theme='https://volantis.js.org/#2.6.5'>
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
    
  
  <!-- 渲染优化 -->
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <!-- 页面元数据 -->
  
    <title>利用信号控制php常驻进程平滑中断思考 - 麦田麦穗</title>
  
    <meta name="keywords" content="php linux">
  
  
    <meta name="description" content="很多场景下我们都需要进程程序在后台一直处理任务，比如队列消费。可采用while true的方式让进程常驻按一定的频次执行任务。但是但我们要重启进程或中止进程时，如何保证进程内正在执行的任务执行完毕再中止呢？">
  

  <!-- feed -->
  

  <!-- import meta -->
  

  <!-- link -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13/css/all.min.css">
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css">

  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css">

  

  

  

  

  <!-- import link -->
  

  
    
      
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10.0.2/styles/monokai-sublime.css">

    
  
  
    
<link rel="stylesheet" href="/css/style.css">

  

  <script>
    function setLoadingBarProgress(num) {
      document.getElementById('loading-bar').style.width=num+"%";
    }
  </script>

  
  
    <!-- ba -->
    <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?e99feed4245dcdf8a16ad3d62e236e14";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
    </script>
  
    
</head>

<body>
  
  <div id="loading-bar-wrapper">
  <div id="loading-bar"></div>
</div>
<header class="l_header shadow blur">
  <div class='container'>
  <div class='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h'>
        <li><a class="s-comment fas fa-comments fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
          <li><a class="s-toc fas fa-list fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/'>
          
          
          
            麦穗
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h'>
          
          
          
            
            
              <li>
                <a class="flat-box" href=/
                  
                  
                  
                    id="home"
                  >
                  <i class='fas fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/categories/
                  
                  
                  
                    id="categories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/tags/
                  
                  
                  
                    id="tags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/archives/
                  
                  
                  
                    id="archives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/friends/
                  
                  
                  
                    id="friends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="flat-box" href=/about/
                  
                  
                  
                    id="about"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
				</ul>
			</div>

      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fas fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>

			<ul class='switcher nav-list-h'>
				
					<li><a class="s-search fas fa-search fa-fw" target="_self" href='javascript:void(0)'></a></li>
				
				<li>
          <a class="s-menu fas fa-bars fa-fw" target="_self" href='javascript:void(0)'></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="flat-box" href=/
                  
                  
                  
                    id="home"
                  >
                  <i class='fas fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/categories/
                  
                  
                  
                    id="categories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/tags/
                  
                  
                  
                    id="tags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/archives/
                  
                  
                  
                    id="archives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/friends/
                  
                  
                  
                    id="friends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="flat-box" href=/about/
                  
                  
                  
                    id="about"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>
		</div>
	</div>
  </div>
</header>

<script>setLoadingBarProgress(40);</script>



  <div class="l_body nocover">
    <div class='body-wrapper'>
      

<div class='l_main'>
  

  
    <article id="post" class="post white-box reveal shadow article-type-post" itemscope itemprop="blogPost">
      


  <section class='meta'>
    
      
      
      <div class="meta" id="header-meta">
        
          
  <h1 class="title">
    <a href="/2020/05/30/php%E5%B8%B8%E9%A9%BB%E8%BF%9B%E7%A8%8B%E5%B9%B3%E6%BB%91%E4%B8%AD%E6%96%AD/">
      利用信号控制php常驻进程平滑中断思考
    </a>
  </h1>


        
        <div class='new-meta-box'>
          
            
          
            
              
<div class='new-meta-item author'>
  <a href="" rel="nofollow">
    <img src="https://vison-blog.oss-cn-beijing.aliyuncs.com/20210603145600.png">
    <p>vison</p>
  </a>
</div>

            
          
            
              

            
          
            
              <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
    <p>发布于：May 30, 2020</p>
  </a>
</div>

            
          
            
              

            
          
        </div>
        
          <hr>
        
      </div>
    
  </section>


      <section class="article typo">
        <div class="article-entry" itemprop="articleBody">
          
          <p>很多场景下我们都需要进程程序在后台一直处理任务，比如队列消费。可采用while true的方式让进程常驻按一定的频次执行任务。但是但我们要重启进程或中止进程时，如何保证进程内正在执行的任务执行完毕再中止呢？</p>
<span id="more"></span>

<p>思考一下，可不可以我们通过一种指令告诉进程，”诶，我现在要重启一下，你能把正在做的事情做完了先退出歇会么？”</p>
<p><code>kill</code>命令可以解决这个问题。</p>
<h2 id="再看kill"><a href="#再看kill" class="headerlink" title="再看kill"></a>再看kill</h2><p>许人肯定会觉得 kill 不就是杀掉进程么？ 我经常用<code>kill -9</code>杀进程。 这么说也没错，不过我们现在可以系统地来看看<code>kill</code>命令.</p>
<p>我们先看下官方的定义,让那个男人来跟我们讲讲。</p>
<pre><code class="shell">man shell</code></pre>
<pre><code>KILL(1)                                                                         User Commands                                                                         KILL(1)

NAME
       kill - terminate a process

SYNOPSIS
       kill [-s signal|-p] [-q sigval] [-a] [--] pid...
       kill -l [signal]

DESCRIPTION
       The command kill sends the specified signal to the specified process or process group.  If no signal is specified, the TERM signal is sent.  The TERM signal will kill
       processes which do not catch this signal.  For other processes, it may be necessary to use the KILL (9) signal, since this signal cannot be caught.

       Most modern shells have a builtin kill function, with a usage rather similar to that of the command described here.  The &#39;-a&#39; and &#39;-p&#39; options, and the possibility to
       specify processes by command name are a local extension.

       If sig is 0, then no signal is sent, but error checking is still performed.</code></pre><ul>
<li>官方解释<code>kill</code>是用来终止进程的。</li>
<li><code>kill</code> 发送指定的信号给到进程或进程组。</li>
<li>如果没有指定信号，默认发送<code>TERM</code>信号。</li>
<li><code>TERM</code>信号将会杀掉进程，当<code>TERM</code>未被捕获的时候。</li>
<li><code>9</code>信号不能被捕获</li>
</ul>
<p><strong>谈谈我的理解</strong></p>
<ol>
<li>kill命令就是用来杀掉进程的</li>
<li>它可以给进程发送一些指令，让程序去捕获做特殊处理。比如上面说到的场景，让程序执行完正在执行的任务，再退出。</li>
</ol>
<h2 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h2><p>接下来我们用PHP脚本来验证下上面的理解。我们用<code>pcntl_signal</code>来对信号进行捕获。</p>
<pre><code class="php">class SignalShell extends Shell
&#123;

    private $taskFinish = false;

    public function __construct()
    &#123;
//        pcntl_signal(SIGTERM, [$this, &#39;sig_handler&#39;]);
//        pcntl_signal(SIGHUP, [$this, &#39;sig_handler&#39;]);
        pcntl_signal(SIGINT, [$this, &#39;sig_handler&#39;]);
//        pcntl_signal(SIGQUIT, [$this, &#39;sig_handler&#39;]);
        pcntl_signal(SIGILL, [$this, &#39;sig_handler&#39;]);
        pcntl_signal(SIGPIPE, [$this, &#39;sig_handler&#39;]);
        pcntl_signal(SIGALRM, [$this, &#39;sig_handler&#39;]);
        pcntl_signal(SIGUSR1, [$this, &#39;sig_handler&#39;]);
        $this-&gt;info(&quot;注册信号&quot;);
    &#125;

    public function task()
    &#123;
        while (true &amp;&amp; !$this-&gt;taskFinish) &#123;
            sleep(10);
            $this-&gt;info(uniqid());
        &#125;
    &#125;

    public function sig_handler($signo)
    &#123;
        $time = date(&#39;Y-m-d H:i:s&#39;);
        if ($signo == 14) &#123;
            //忽略alarm信号
            echo $time . &quot; ignore alarm signo[&#123;$signo&#125;]\r\n&quot;;
        &#125; else &#123;
            echo $time . &quot; exit  signo[&#123;$signo&#125;]\r\n&quot;;
            if ($signo == SIGUSR1) &#123;
                $this-&gt;info(&quot;捕获自定义&quot;);
                $this-&gt;taskFinish = true;
            &#125;
        &#125;
    &#125;
&#125;</code></pre>
<p>代码很简单，就是让脚本每隔10秒输出一个字符串，任务之前对一些信号进行捕获。</p>
<p><img src="http://img.rc5j.cn/blog20200530114817.png" alt=""></p>
<p>分别对进程执行了，kill、kill QUIT、kill HUP 发现进程都会被直接终止。</p>
<p>下面我们开始,对<code>TERM</code>进行捕获。</p>
<pre><code class="php">pcntl_signal(SIGTERM, [$this, &#39;sig_handler&#39;]);</code></pre>
<p>打开注释。</p>
<p><img src="http://img.rc5j.cn/blog20200530115128.png" alt=""></p>
<p>15信号(TERM)被捕获到了，但是进程并没有退出,还再继续执行。</p>
<p>我们再试下USR1信号。</p>
<p><img src="http://img.rc5j.cn/blog20200530115632.png" alt=""></p>
<p>USR1被捕获到了，并且程序立即执行完一次输出退出了。注意我是程序自己控制了,捕获到USR1之后不继续执行循环。</p>
<p>细心的朋友们可能会发现，程序中的sleep被跳过了。</p>
<p><strong>这是什么原因呢?</strong></p>
<h2 id="sleep"><a href="#sleep" class="headerlink" title="sleep"></a>sleep</h2><p>事实上sleep是一个特殊的函数。其实官方文档有解释:</p>
<p><img src="http://img.rc5j.cn/blog20200530120859.png" alt=""></p>
<p><code>sleep</code>在被信号中止时，会返回非0值,非windows下会返回剩余秒数。</p>
<p>让我们来验证下。</p>
<pre><code class="php"> while (true &amp;&amp; !$this-&gt;taskFinish) &#123;
            $res = sleep(10);
            $this-&gt;info(&quot;sleep返回:&quot;.$res);
            $this-&gt;info(uniqid());
 &#125;</code></pre>
<p>我们记录了sleep执行完的返回值。</p>
<p><img src="http://img.rc5j.cn/blog20200530121207.png" alt=""></p>
<p>发现信号给到时，sleep确实会返回剩余秒数。这就解释了为什么上面看到的sleep被跳过了。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><blockquote>
<p>A process can define how to handle incoming POSIX signals. If a process does not define a behaviour for a signal, then the default handler for that signal is being used. The table below lists some default actions for POSIX-compliant UNIX systems, such as FreeBSD, OpenBSD and Linux.</p>
</blockquote>
<ol>
<li>kill能给进程发送信号量，告诉进程按什么方式结束。</li>
<li>kill定义的不同信号量，用法不同，但是需要程序自己去处理。它只是定义了目的，但未定义过程和实际结果。</li>
</ol>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Signal_(IPC)">https://en.wikipedia.org/wiki/Signal_(IPC)</a></p>

          
            <div class='article_footer'>
              
                
  
    
    



  

  
    
    



  

  
    
    

<section class="widget copyright  desktop mobile">
  <div class='content'>
    
      <blockquote>
        
          
            <p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p>

          
        
          
            <p>本文永久链接是：<a href=http://blog.visonforcoding.xyz/2020/05/30/php%E5%B8%B8%E9%A9%BB%E8%BF%9B%E7%A8%8B%E5%B9%B3%E6%BB%91%E4%B8%AD%E6%96%AD/>http://blog.visonforcoding.xyz/2020/05/30/php%E5%B8%B8%E9%A9%BB%E8%BF%9B%E7%A8%8B%E5%B9%B3%E6%BB%91%E4%B8%AD%E6%96%AD/</a></p>
          
        
      </blockquote>
    
  </div>
</section>

  

  
    
    

<section class="widget qrcode  desktop mobile">
  

  <div class='content article-entry'>
    
      
        <div class='fancybox'><img src='https://vison-blog.oss-cn-beijing.aliyuncs.com/20210430145030.png'
        
          height='240px'
        ></div>
      
    
    请我喝杯咖啡
  </div>
</section>

  


              
            </div>
          
        </div>
        
          


  <section class='meta' id="footer-meta">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-05-30T12:23:51+08:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：May 30, 2020</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/tags/php-linux/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>php linux</p></a></div>


        
      
        
          

        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://blog.visonforcoding.xyz/2020/05/30/php%E5%B8%B8%E9%A9%BB%E8%BF%9B%E7%A8%8B%E5%B9%B3%E6%BB%91%E4%B8%AD%E6%96%AD/&title=利用信号控制php常驻进程平滑中断思考 - 麦田麦穗&summary=很多场景下我们都需要进程程序在后台一直处理任务，比如队列消费。可采用while true的方式让进程常驻按一定的频次执行任务。但是但我们要重启进程或中止进程时，如何保证进程内正在执行的任务执行完毕再中止呢？"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/qq.png">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=http://blog.visonforcoding.xyz/2020/05/30/php%E5%B8%B8%E9%A9%BB%E8%BF%9B%E7%A8%8B%E5%B9%B3%E6%BB%91%E4%B8%AD%E6%96%AD/&title=利用信号控制php常驻进程平滑中断思考 - 麦田麦穗&summary=很多场景下我们都需要进程程序在后台一直处理任务，比如队列消费。可采用while true的方式让进程常驻按一定的频次执行任务。但是但我们要重启进程或中止进程时，如何保证进程内正在执行的任务执行完毕再中止呢？"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/qzone.png">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=http://blog.visonforcoding.xyz/2020/05/30/php%E5%B8%B8%E9%A9%BB%E8%BF%9B%E7%A8%8B%E5%B9%B3%E6%BB%91%E4%B8%AD%E6%96%AD/&title=利用信号控制php常驻进程平滑中断思考 - 麦田麦穗&summary=很多场景下我们都需要进程程序在后台一直处理任务，比如队列消费。可采用while true的方式让进程常驻按一定的频次执行任务。但是但我们要重启进程或中止进程时，如何保证进程内正在执行的任务执行完毕再中止呢？"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/logo/128/weibo.png">
          
        </a>
      
    
  </div>
</div>



        
      
    </div>
  </section>


        
        
          <div class="prev-next">
            
              <a class='prev' href='/2020/06/01/MyBatis-Dynamic-SQL%E4%BD%BF%E7%94%A8%E5%B0%9D%E8%AF%95/'>
                <p class='title'><i class="fas fa-chevron-left" aria-hidden="true"></i>MyBatis Dynamic SQL使用尝试</p>
                <p class='content'>首先说明下，我可能是被这个插件的名字给误导了。我原本以为Dynamic SQL是为了解决动态查询场景的。但可能MyBatis Dynamic SQL 并不是来解决这个问题的。
20.06.04更...</p>
              </a>
            
            
              <a class='next' href='/2020/05/26/spring-boot%E5%88%86%E7%8E%AF%E5%A2%83%E8%87%AA%E5%AE%9A%E4%B9%89%E9%85%8D%E7%BD%AE/'>
                <p class='title'>spring boot分环境自定义配置<i class="fas fa-chevron-right" aria-hidden="true"></i></p>
                <p class='content'>在一般规模企业，应当都有测试环境和正式环境区别，或者至少也有开发环境和正式环境。那不同环境必然就会有一些环境依赖的不同，不管是出于安全性考虑还是其他原因导致的。比如数据库配置、OSS账号信息等。...</p>
              </a>
            
          </div>
        
      </section>
    </article>
  

  
    <!-- 显示推荐文章和评论 -->



  <article class="post white-box reveal comments shadow">
    <section class="article typo">
      <p ct><i class='fas fa-comments'></i> 评论</p>
      
      
      
      
        <section id="comments">
          <div id="gitalk-container"></div>
        </section>
      
      
      
      
    </section>
  </article>


  




<!-- 根据页面mathjax变量决定是否加载MathJax数学公式js -->



  <script>
    window.subData = {
      title: '利用信号控制php常驻进程平滑中断思考',
      tools: true
    }
  </script>


</div>
<aside class='l_side'>
  
  

  
    
    


  <section class="widget toc-wrapper shadow desktop mobile">
    
  <header>
    
      <i class="fas fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%8D%E7%9C%8Bkill"><span class="toc-text">再看kill</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%AA%8C%E8%AF%81"><span class="toc-text">验证</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#sleep"><span class="toc-text">sleep</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-text">总结</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%82%E8%80%83"><span class="toc-text">参考</span></a></li></ol>
    </div>
  </section>


  


</aside>


  
  <footer class="clearfix">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper">
          
            
              <a href="/atom.xml"
                class="social fas fa-rss flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="mailto:visonforcoding@gmail.com"
                class="social fas fa-envelope flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
            
              <a href="https://music.163.com/#/user/home?id=63035382"
                class="social fas fa-headphones-alt flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
              </a>
            
          
        </div>
      
    
      
        <div><p>Blog content follows the <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en">Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License</a></p>
</div>
      
    
      
        Use
        <a href="https://volantis.js.org/" target="_blank" class="codename">Volantis</a>
        as theme, total visits
          <span id="busuanzi_value_site_pv"><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span>
          times
        
      
    
      
        <div class='copyright'>
        <p><a target="_blank" rel="noopener" href="https://visonforcoding.github.io">Copyright © 2017-2020 Vison</a></p>

        </div>
      
    
  </footer>

<script>setLoadingBarProgress(80);</script>


      <script>setLoadingBarProgress(60);</script>
    </div>
    <a class="s-top fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
  </div>
  
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4/dist/jquery.min.js"></script>


  <script>
    
    var SEARCH_SERVICE = "hexo" || "hexo";
    var ROOT = "/" || "/";
    if (!ROOT.endsWith('/')) ROOT += '/';
  </script>


  <script async src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2/js/instant_page.js" type="module" defer integrity="sha384-OeDn4XE77tdHo8pGtE1apMPmAipjoxUQ++eeJa6EtJCfHlvijigWiJpD7VDPWXV1"></script>


  <script src="https://cdn.jsdelivr.net/npm/scrollreveal@4.0.6/dist/scrollreveal.min.js"></script>
  <script type="text/javascript">
    $(function() {
      ScrollReveal().reveal('.l_main .reveal', {
        distance: '8px',
        duration: '800',
        interval: '100',
        scale: '1'
      });
    });
  </script>


  
<script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script>

  <script type="text/javascript">
    $(function() {
      Waves.attach('.flat-btn', ['waves-button']);
      Waves.attach('.float-btn', ['waves-button', 'waves-float']);
      Waves.attach('.float-btn-light', ['waves-button', 'waves-float', 'waves-light']);
      Waves.attach('.flat-box', ['waves-block']);
      Waves.attach('.float-box', ['waves-block', 'waves-float']);
      Waves.attach('.waves-image');
      Waves.init();
    });
  </script>


  <script async src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js"></script>



  
  
  
    
<script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script>

    <script type="text/javascript">
      $(function(){
        var imgs=["https://cdn.jsdelivr.net/gh/xaoxuu/cdn-wallpaper/abstract/41F215B9-261F-48B4-80B5-4E86E165259E.jpeg"];
        if ('true' == 'true') {
          function shuffle(arr){
            /*From countercurrent-time*/
            var n = arr.length;
            while(n--) {
              var index = Math.floor(Math.random() * n);
              var temp = arr[index];
              arr[index] = arr[n];
              arr[n] = temp;
            }
          }
          shuffle(imgs);
        }
        if ('.cover') {
          $('.cover').backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        } else {
          $.backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        }
      });
    </script>
  








  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
  <script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
  <script type="text/javascript">
    var gitalk = new Gitalk({
      clientID: "bd4d3e0f680affe497d0",
      clientSecret: "a31430007d9952541a4322ebf7219a0d127cc57d",
      repo: "visonforcoding.github.io",
      owner: "visonforcoding",
      admin: "visonforcoding",
      
        id: (location.pathname.split("/").slice(-2)[0]).substring(0,49),      // Ensure uniqueness and length less than 50
      
      distractionFreeMode: false  // Facebook-like distraction free mode
    });
    gitalk.render('gitalk-container');
  </script>








  
<script src="/js/app.js"></script>



  
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2.6.5/js/search.js"></script>



  
<script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-volantis@2/js/comment_typing.js"></script>




  
    
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/highlight.min.js"></script>

    <script>hljs.initHighlightingOnLoad();</script>
  



<!-- 复制 -->

  <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="fas fa-copy"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('fa-copy');
        $icon.addClass('fa-check-circle');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('fa-check-circle');
          $icon.addClass('fa-copy');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('fa-copy');
        $icon.addClass('fa-times-circle');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('fa-times-circle');
          $icon.addClass('fa-copy');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>




<!-- fancybox -->

  <script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<script>
  function pjax_fancybox() {
    let LAZY_LOAD_IMAGE = "";
    $(".article-entry").find("img").not('.inline').not('a img').each(function () { //渲染 fancybox
      var element = document.createElement("a"); // a 标签
      $(element).attr("pjax-fancybox", "");  // 过滤 pjax
      if (LAZY_LOAD_IMAGE) {
        $(element).attr("href", $(this).attr("data-original"));
      }
      $(element).attr("data-fancybox", "images");
      var caption = "";   // 描述信息
      if ($(this).attr('alt')) {  // 标准 markdown 描述信息
        $(element).attr('data-caption', $(this).attr('alt'));
        caption = $(this).attr('alt');
      }
      var div = document.createElement("div");
      $(div).addClass("fancybox");
      $(this).wrap(div); // 最外层套 div ，其实主要作用还是 class 样式
      var span = document.createElement("span");
      $(span).addClass("image-caption");
      $(span).text(caption); // 加描述
      $(this).after(span);  // 再套一层描述
      $(this).wrap(element);  // 最后套 a 标签
    })
    $(".article-entry").find("img").fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: false,
      closeClick: true,
      helpers: {
        overlay: {closeClick: true}
      },
      buttons: [
        "zoom",
        "close"
      ]
    });
  };
  $(function () {
    pjax_fancybox();
  });
</script>






  <script>setLoadingBarProgress(100);</script>  
</body>
</html>
